package com.xiaoma.pattern.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;



/**
 * 每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时，
 * 方法调用被编码并发送到其调用处理程序的invoke方法。
 *
 * @author maj
 * @date 2018年8月25日 上午9:24:53
 * <p>
 * 这里的target可以是任何类型的，不像静态代理那样硬编码的。所以可以代理多种接口
 */
public class MyInvocationhandler implements InvocationHandler {

    /**
     * JDK 中生成代理对象主要涉及两个类，第一个类为 java.lang.reflect.Proxy，通过静态方法 newProxyInstance 生成代理对象，
     * 第二个为 java.lang.reflect.InvocationHandler 接口，通过 invoke 方法对业务进行增强；
     * <p>
     * 作者：架构小菜
     * 链接：https://www.jianshu.com/p/45a37c83333c
     * 来源：简书
     * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     */

    private Object target; // 被代理类

    private Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object clazz) {
        this.target = clazz;
    }

    /**
     * @author xiaomacdut
     * @version 0.0.1
     * @data 2020年3月2日 上午9:45:19
     * @since 0.0.1
     */
    public <T> T getProxyInstance(){
        //从生成的jad文件可以看出，生成的代理对象是实现了我们指定的接口的,也就是第二个参数
        T t= (T)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

        return t;
    }

    /**
     * @param proxy:动态生成的代理对象,对应上面的proxyInstance
     * @param method:调用的方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("生成的代理类:" + proxy.getClass().getName());
        String methodName = method.getName();
        Object obj = null;

     /* if("savePerson".equals(methodName) || "deletePerson".equals(methodName) || "updatePerson".equals(methodName)){
            // this.transaction.beginTransaction(); // 开启事务
            // 在调用目标方法前后可以做其他的事情
           obj=method.invoke(target);
        }else{
            obj= method.invoke(target);
        }*/

        //上面if else 可以用策略模式来优化，无须修改代理类的代码
        Object o = context.executeStrategy(proxy, method, args, target);

        return obj;
    }

}
